#include<sys/socket.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<pthread.h>
#include<assert.h>
#include<string.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/select.h>
#include<sys/time.h>
#include<errno.h>
#include<stdio.h>

#include "elog.h"
#include "ipc_evt_api.h"
#include "ipc_event_types.h"

// socket for communication between application and daemon server
static int ipc_evt_handle = -1;
// event notification callback
static IPC_EVT_API_CALLBACK ipc_evt_callback_ptr = 0; 

// const values definition
static const unsigned short DEFAULT_IPC_EVT_PORT = 10006u;
static const char * DEFAULT_IPC_EVT_IP = "127.0.0.1";
static const int DEFAULT_IPC_MSG_HDR_LEN = 12;
static const int DEFAULT_IPC_MSG_MAX_LEN =1024*4;/// 102400;

static char LOG_TAG[128] = "IPC_APP";


static pthread_mutex_t __IPC_PUBLISHER_MUTEX__ = PTHREAD_MUTEX_INITIALIZER;

/*
 *******************************************************************************
 *
 * @param Implementation Notes:
 *        None.
 *
 *******************************************************************************
 */
static int compileInt(char * buffer , int offset , int value , int length)
{
	uint32_t data = htonl((uint32_t)value);
  
  (void)memcpy(buffer + offset , (void*)&data , sizeof(data));
  
  return length;
}

/*
 *******************************************************************************
 *
 * @param Implementation Notes:
 *        None.
 *
 *******************************************************************************
 */
static int compileBytes(char * buffer , int offset , void * data , int length)
{
  (void)memcpy(buffer + offset , data , (unsigned int)length);
  
  return length;
}

/*
 *******************************************************************************
 *
 * @param Implementation Notes:
 *        None.
 *
 *******************************************************************************
 */
static int parseInt(char * buffer , int offset , int * pvalue , int length)
{	
  (void)memcpy(pvalue , buffer + offset , (unsigned int)length);
  
  *pvalue = ntohl(*pvalue);
  
  return length;
}

/*
 *******************************************************************************
 *
 * @param Implementation Notes:
 *        None.
 *
 *******************************************************************************
 */
static int parseBytes(char * buffer , int offset , void * data , int length)
{
  (void)memcpy(data , buffer + offset , (unsigned int)length);
  
  return length;
}

/*
 *******************************************************************************
 *
 * @param Implementation Notes:
 *        None.
 *
 *******************************************************************************
 */
int initialize_ipc_evt(IPC_EVT_API_CALLBACK funcPtr, const char *log_tag) /* parasoft-suppress PSA_REGLESSW_ST-12_006 "Risks of introducing bugs, " */
{
  strncpy(LOG_TAG, log_tag, sizeof(LOG_TAG));
  assert(funcPtr);
  ipc_evt_callback_ptr = funcPtr;//回调函数指针
  int error_time=10;
connect_begin:
  error_time=10;
  if (ipc_evt_handle != -1)
  {
    return -1;
  }
  
  ipc_evt_handle = socket(AF_INET , SOCK_STREAM , 0);//创建客户端socket
  if (ipc_evt_handle == -1)
  {
    LOGE(LOG_TAG, "socket create failed(%s)!!\n", strerror(errno));
    return -1;
  }

  struct sockaddr_in addr;  //服务器端网络地址结构体 
  (void)memset(&addr , 0x00 , sizeof(addr));
  
  addr.sin_family = AF_INET; /* parasoft-suppress MM_MISRA-0044a "Both struct and macro are defined in extern system library" */
  addr.sin_port = htons(DEFAULT_IPC_EVT_PORT);
  addr.sin_addr.s_addr = inet_addr(DEFAULT_IPC_EVT_IP);
  
  /* connect用来将参数ipc_evt_handle的socket 连至参数serv_addr 指定的网络地址 */
  while (connect(ipc_evt_handle , (struct sockaddr *)&addr , sizeof(addr)) == -1)
  {
    usleep(1000*300);
    printf("connect ipc event failed wait IPC server init:%d\n",error_time);
    LOGE(LOG_TAG, "socket connect failed(%s)!!\n", strerror(errno));
    if(error_time)
    {
      error_time--;
      if(error_time==0)
      {
        if (ipc_evt_handle != -1)
        {
          close(ipc_evt_handle);
          ipc_evt_handle = -1;
          usleep(1000*500);
        }
        goto connect_begin;
      }
    }
  }
  
  return 0;
}

/*
 *******************************************************************************
 *
 * @param Implementation Notes:
 *        None.
 *
 *******************************************************************************
 */
int finalize_ipc_evt() //断开连接ipc_evt_handle
{
  if (ipc_evt_handle != -1)
  {		
    (void)close(ipc_evt_handle);
    ipc_evt_handle = -1;
    printf("#####finalize_ipc_evt###\n");
    return 0;
  }
  
  return -1;
}

/*
 *******************************************************************************
 *
 * @param Implementation Notes:
 *        None.
 *
 *******************************************************************************
 */
int subscribe_ipc_evt(int type) //注册请求
{
  if (ipc_evt_handle == -1)
  {
    return -1;
  }
  
  char msg[DEFAULT_IPC_MSG_HDR_LEN];
  int length = DEFAULT_IPC_MSG_HDR_LEN;
  int cmd = IPC_EVENT_TYPE_SUBSCRIBE;
  int offset = 0;
  memset(msg,0,DEFAULT_IPC_MSG_HDR_LEN);
  offset += compileInt(msg , offset , length , 4);
  offset += compileInt(msg , offset , cmd , 4);
  offset += compileInt(msg , offset , type , 4);
  
  int bytes = 0,error_v=0;
  do
  {
    int ret = send(ipc_evt_handle , (char *)msg + bytes , length - bytes , MSG_NOSIGNAL);
    if (ret == -1)
    {
        	 error_v=errno;
      if (error_v == EINTR||error_v==EAGAIN)
      {
        continue;
      }
      else
      {
        (void) finalize_ipc_evt();
        
        return -1;
      }
    }
    
    bytes += ret;
  
  } while (bytes < length);
  
  return 0;
}

/*
 *******************************************************************************
 *
 * @param Implementation Notes:
 *        None.
 *
 *******************************************************************************
 */
int unsubscribe_ipc_evt(int type)
{
  if (ipc_evt_handle == -1)
  {
    return -1;
  }
  
  char msg[DEFAULT_IPC_MSG_HDR_LEN];
  int length = DEFAULT_IPC_MSG_HDR_LEN;
  int cmd = IPC_EVENT_TYPE_UNSUBSCRIBE;
  int offset = 0;
   memset(msg,0,DEFAULT_IPC_MSG_HDR_LEN);
  offset += compileInt(msg , offset , length , 4);
  offset += compileInt(msg , offset , cmd , 4);
  offset += compileInt(msg , offset , type , 4);
  
  // send the data in a loop in case that the socket buffer is full.
  int bytes = 0,error_v=0;
  do
  {
    int ret = send(ipc_evt_handle , (char *)msg + bytes , length - bytes , MSG_NOSIGNAL);
    if (ret == -1)
    {
   	 error_v=errno;
      if (error_v == EINTR||error_v==EAGAIN)
      {
        continue;
      }
      else
      {
        LOGE(LOG_TAG, "socket send failed(%s)!!\n", strerror(errno));
        (void) finalize_ipc_evt();
        
        return -1;
      }
    }
    
    bytes += ret;
  
  } while (bytes < length);
  
  return 0;
}

/*
 *******************************************************************************
 *
 * @param Implementation Notes:
 *        None.
 *
 *******************************************************************************
 */
int publish_ipc_evt(int type , void * data , int length)//带应答与应答的消息内容的发送函数
{
  if (pthread_mutex_lock(&__IPC_PUBLISHER_MUTEX__) != 0)
  {
    (void)printf("publish_ipc_evt: fail to lock publish mutex,"
           "fail to send 0x%0x. [errno = %d]\n", type, errno);
    LOGE(LOG_TAG, "publish_ipc_evt pthread_mutex_lock failed(%s)!!\n", strerror(errno));
    return -1;
  }

  if (ipc_evt_handle == -1 || 
    (length > DEFAULT_IPC_MSG_MAX_LEN - DEFAULT_IPC_MSG_HDR_LEN))
  {
    (void)pthread_mutex_unlock(&__IPC_PUBLISHER_MUTEX__);
    LOGE(LOG_TAG, "publish_ipc_evt ipc_evt_handle(%d) length(%d)!!\n", ipc_evt_handle, length);
	
    return -1;
  }

  /* 填充消息体 */
  char msg[DEFAULT_IPC_MSG_MAX_LEN];  
  int len = DEFAULT_IPC_MSG_HDR_LEN + length;
  int cmd = IPC_EVENT_TYPE_PUBLISH;
  int offset = 0,error_v=0;
   memset(msg,0,DEFAULT_IPC_MSG_MAX_LEN);
  offset += compileInt(msg , offset , len , 4);
  offset += compileInt(msg , offset , cmd , 4);
  offset += compileInt(msg , offset , type ,4);
  if (length > 0)
  {
    offset += compileBytes(msg , offset , data , length);
  }
  
  // send the data in a loop in case that the socket buffer is full.
  int bytes = 0;
  do
  { /* 发送消息 */
    int ret = send(ipc_evt_handle , (char *)msg + bytes , len - bytes , MSG_NOSIGNAL);
    if (ret == -1)
    {
    	error_v=errno;
      if (error_v == EINTR||error_v==EAGAIN)
      {
        continue;
      }
      else
      {
        LOGE(LOG_TAG, "socket send failed(%s)!!\n", strerror(errno));
        (void) finalize_ipc_evt();

        (void)pthread_mutex_unlock(&__IPC_PUBLISHER_MUTEX__);
        
        return -1;
      }
    }
    
    bytes += ret;
  
  } while (bytes < length);	

  (void)pthread_mutex_unlock(&__IPC_PUBLISHER_MUTEX__);
  
  return 0;
}

/*
 *******************************************************************************
 *
 * @param Implementation Notes:
 *        None.
 *
 *******************************************************************************
 */
int handle_ipc_evt_message(void)//读取外部请求消息数据
{
  char msgHdr[DEFAULT_IPC_MSG_HDR_LEN];
   memset(msgHdr,0,DEFAULT_IPC_MSG_HDR_LEN);
  // recv the data in a loop in case that the socket buffer is full.
  int bytes = 0,error_v=0;
  do
  {
    int ret = recv(ipc_evt_handle , msgHdr + bytes , DEFAULT_IPC_MSG_HDR_LEN - bytes , 0);//读取数据
    if (ret <= 0)
    {
    	error_v=errno;
       if (error_v != EAGAIN&&error_v != EINTR&&error_v != EWOULDBLOCK)
      {
        LOGE(LOG_TAG, "socket recv failed(%s)!!\n", strerror(errno));
        (void)finalize_ipc_evt();
        
        return -1;
      }
      else
      {
        continue;
      }
    }
    
    bytes += ret;
  
  } while (bytes < DEFAULT_IPC_MSG_HDR_LEN);
  
  int length = -1;
  int cmd = -1;
  int type = -1;
  
  int offset = 0;
  offset += parseInt(msgHdr , offset , &length , 4);
  offset += parseInt(msgHdr , offset , &cmd , 4);
  offset += parseInt(msgHdr , offset , &type , 4);
  
  if (cmd != IPC_EVENT_TYPE_PUBLISH)
  {
    return 0;
  }
  
  char msgBody[DEFAULT_IPC_MSG_MAX_LEN];
   memset(msgBody,0,DEFAULT_IPC_MSG_MAX_LEN);
  if ((length - DEFAULT_IPC_MSG_HDR_LEN) > 0)
  {
    // recv the data in a loop in case that the socket buffer is full.
    bytes = 0;
    do
    {
      int ret = recv(ipc_evt_handle , msgBody + bytes , length - DEFAULT_IPC_MSG_HDR_LEN - bytes , 0);
      if (ret <= 0)
      {
      	error_v=errno;
        if (error_v != EAGAIN&&error_v != EINTR&&error_v != EWOULDBLOCK)
        {
          LOGE(LOG_TAG, "socket recv failed(%s)!!\n", strerror(errno));
          (void)finalize_ipc_evt();
          
          return -1;
        }
        else
        {
          continue;
        }
      }
      bytes += ret;
    } while (bytes < (length - DEFAULT_IPC_MSG_HDR_LEN));
  }
  
  return ipc_evt_callback_ptr(type , msgBody , length - DEFAULT_IPC_MSG_HDR_LEN);//回调函数指针
}

/*
 *******************************************************************************
 *
 * @param Implementation Notes:
 *        None.
 *
 *******************************************************************************
 */
int get_ipc_evt_handle()
{
  return ipc_evt_handle;
}

/*
 *******************************************************************************
 *
 * @param Implementation Notes:
 *        None.
 *
 *******************************************************************************
 */
void run_ipc_evt_loop()
{
  while (1)
  {
    fd_set ipc_evt_fds_m;
    FD_ZERO(&ipc_evt_fds_m);//FD_ZERO将所有位置零	
    FD_SET(ipc_evt_handle , &ipc_evt_fds_m);//设置变量的某个位置位
    
    struct timeval tv;
    tv.tv_sec =3;/// 2;
    tv.tv_usec = 0;
    
    int ret = select(ipc_evt_handle + 1 , &ipc_evt_fds_m , 0 , 0 , &tv);/*拥塞等待文件描述符事件的到来；
    													如果超过设定的时间，则不再等待，继续往下执行。*/
    if (ret == -1)//失败
    {
      if (errno == EINTR)
      {
        continue;
      }
      sleep(1);
      //break;
    }
    else if (ret == 0)//超时
    {
      // timeout occur
      continue;
    }
    else//成功
    {
      if (FD_ISSET(ipc_evt_handle , &ipc_evt_fds_m))//FD_ISSET测试给定位是否置位
      {
        if (handle_ipc_evt_message() == -1)//读取数据
        {
          LOGE(LOG_TAG, "handle_ipc_evt_message failed(%s)!!\n", strerror(errno));
          return;
        }
      }
    }
  }
}


